package com.hunttown.mes.manage.controller._lbs;

import com.hunttown.mes.common.OperateCode;
import com.hunttown.mes.common.http.HttpClient;
import com.hunttown.mes.common.keys.KeyConstants;
import com.hunttown.mes.manage.controller.common.AdminBaseClass;
import com.hunttown.mes.manage.controller.common.ModelService;
import com.hunttown.mes.rpc.domain.*;
import com.hunttown.mes.rpc.domain.query.LbsPointDTOQuery;
import com.hunttown.mes.rpc.domain.query.LbsTerminalDTOQuery;
import com.hunttown.mes.rpc.domain.query.LbsTrackDTOQuery;
import com.hunttown.common.domain.Page;
import com.hunttown.mes.common.utils.*;
import com.hunttown.mes.manage.service.*;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

import static com.hunttown.mes.common.utils.PageUtils.PagePackage;

/**
 * created by wangjunfu on 2021-07-05 11:06:22
 */
@Controller
@RequestMapping(value = "/lbs/track")
public class LbsTrackController {

    private final static Logger logger = LoggerFactory.getLogger(LbsTrackController.class);

    private final LbsServiceManageService lbsServiceService;
    private final LbsTerminalManageService lbsTerminalService;
    private final LbsTrackManageService lbsTrackService;
    private final LbsPointManageService lbsPointService;
    private final AnalysisManageManageService manageService;
    private final AnalysisConfigParamManageService configParamService;
    private final ModelService modelService;

    @Autowired
    public LbsTrackController(LbsServiceManageService lbsServiceService, LbsTerminalManageService lbsTerminalService, LbsTrackManageService lbsTrackService, LbsPointManageService lbsPointService, AnalysisManageManageService manageService, AnalysisConfigParamManageService configParamService, ModelService modelService) {
        this.lbsServiceService = lbsServiceService;
        this.lbsTerminalService = lbsTerminalService;
        this.lbsTrackService = lbsTrackService;
        this.lbsPointService = lbsPointService;
        this.manageService = manageService;
        this.configParamService = configParamService;
        this.modelService = modelService;
    }

    //region 配置中的高德信息
    @Value("${amap.key}")
    private String amapKey;

    @Value("${amap.version}")
    private String amapVersion;
    //endregion

    //region 数据列表

    /**
     * 首页
     *
     * @param model    model模型
     * @param dtoQuery 查询条件
     * @return model
     */
    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index(Model model, LbsTrackDTOQuery dtoQuery) {

        dtoQuery.setOrderBy("order by id desc");
        Page<LbsTrackDTO> dataPage = lbsTrackService.getForPage(dtoQuery);

        for (LbsTrackDTO item : dataPage.getItems()) {
            item.setTerminalName(lbsTerminalService.getTerminalName(item.getTerminalId()));
            item.setUserName(modelService.getUserTrueName(item.getUserId()));
        }

        model.addAttribute("page", dataPage);
        model.addAttribute("PagePackage", PagePackage(dataPage.getPn(), dataPage.getStart(), dataPage.getPageSize(), dataPage.getTotalPages(), dataPage.getTotalItems()));
        model.addAttribute("dtoQuery", dtoQuery);

        return "lbs/track/index";
    }
    //endregion

    //region 编辑&保存

    /**
     * 编辑
     *
     * @param model model模型
     * @param id    主键id
     * @return model
     */
    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public String edit(Model model, Integer id) {
        id = (id == null ? 0 : id);

        LbsTrackDTO obj;

        if (id > 0) {
            obj = lbsTrackService.getById(id);
            obj.setUserName(modelService.getUserTrueName(obj.getUserId()));
        } else {
            obj = new LbsTrackDTO();
            obj.setId(0);
            obj.setUserId(0);
            obj.setCarId(0);
        }
        model.addAttribute("Obj", obj);

        //终端信息
        LbsTerminalDTOQuery terminalQuery = new LbsTerminalDTOQuery();
        terminalQuery.setStopFlag(0);
        Page<LbsTerminalDTO> terminalPage = lbsTerminalService.getForPage(terminalQuery);
        model.addAttribute("terminalPage", terminalPage);

        return "lbs/track/edit";
    }

    /**
     * 保存 ajax异步
     *
     * @param request sevelet请求
     * @param objDTO  实体类
     * @return json
     */
    @RequestMapping(value = "/saveEdit", method = RequestMethod.POST)
    @ResponseBody
    public String saveEditNotify(HttpServletRequest request, LbsTrackDTO objDTO) {

        // 结果返回 结果至少包含一个操作状态和一条状态信息
        String result = "";
        Map<String, String> map = new HashMap<String, String>();

        try {
            //必须选择终端
            if (objDTO.getTerminalId() <= 0) {
                map = OperateCode.CompositionResult(map, -10, "请选择终端！");
                result = JSONUtils.toJSON(map);
                return result;
            }

            //选择【终端项目】获取service_id
            LbsTerminalDTO terminal = lbsTerminalService.getFromCacheId(objDTO.getTerminalId());
            if (terminal == null) {
                map = OperateCode.CompositionResult(map, -10, "终端异常！");
                result = JSONUtils.toJSON(map);
                return result;
            }
            objDTO.setServiceId(terminal.getServiceId());

            //region 【高德】调用高德接口生成唯一Key
            LbsServiceDTO service = lbsServiceService.getFromCacheId(terminal.getServiceId());

            if (service != null && service.getLbsType() == 1 && StringUtils.isBlank(objDTO.getTrackKey())) {
                //如果trackkey为空，则生成
                if (StringUtils.isBlank(objDTO.getTrackKey())) {
                    String url = "https://tsapi.amap.com/v1/track/trace/add";
                    String sb = "key=" + KeyConstants.LBS_GAODE_TRACKKEY +
                            "&sid=" + service.getServiceKey() +
                            "&tid=" + terminal.getTerminalKey() +
                            "&trname=" + objDTO.getTrackCode();
                    String res = HttpClient.doPost(url, sb, 1);

                    //解析结果：{"data":{"trname":"eq24tg645265te3t42","trid":20},"errcode":10000,"errdetail":null,"errmsg":"OK"}
                    JSONObject jsonObject = new JSONObject(res);
                    if (!jsonObject.get("errcode").equals(10000)) {
                        map = OperateCode.CompositionResult(map, -10, "高德接口调用失败：" + jsonObject.get("errdetail"));
                        result = JSONUtils.toJSON(map);
                        return result;
                    }

                    JSONObject dataObject = jsonObject.getJSONObject("data");
                    objDTO.setTrackKey(dataObject.get("trid") + "");
                }
            }
            //endregion

            if (objDTO.getId() == null || objDTO.getId() <= 0) {
                //新增
                //设置初始值
                String pin = AdminBaseClass.getPin(request);
                AnalysisManageDTO curr_obj = manageService.getFromCachePin(pin);
                objDTO.setAdminId(curr_obj.getId());
                objDTO.setCreateTime(new Date());
                objDTO.setStopFlag(0);
                objDTO.setDeleteFlag(0);
                objDTO = lbsTrackService.insert(objDTO);

                if (objDTO == null || objDTO.getId() <= 0) {
                    //操作失败
                    map = OperateCode.CompositionResult(map, -1);
                    result = JSONUtils.toJSON(map);
                    return result;
                }

            } else {
                //更新
                //将不需要更新的字段设置为NULL
                Boolean isNext = lbsTrackService.updateInfoById(objDTO);

                if (!isNext) {
                    //操作失败
                    map = OperateCode.CompositionResult(map, -1);
                    result = JSONUtils.toJSON(map);
                    return result;
                }
            }

            //操作成功 结果返回
            map = OperateCode.CompositionResult(map, 1);
            result = JSONUtils.toJSON(map);
            return result;

        } catch (Exception e) {
            //清空map
            map.clear();
            map = OperateCode.CompositionResult(map, -1);
            result = JSONUtils.toJSON(map);
            return result;
        }
    }
    //endregion

    //region 删除&改变状态

    /**
     * 通过id删除 ajax异步
     *
     * @param id 记录id
     * @return json
     */
    @RequestMapping(value = "/deleteById", method = RequestMethod.POST)
    @ResponseBody
    public String deleteNotify(Integer id) {

        // 结果返回结果至少包含一个操作状态和一条状态信息
        String result = "";
        Map<String, String> map = new HashMap<String, String>();

        if (id == null || id <= 0) {
            map = OperateCode.CompositionResult(map, -10, "请选择要删除的记录！");
            result = JSONUtils.toJSON(map);
            return result;
        }

        try {
            Boolean isNext = lbsTrackService.delete(id);
            map = OperateCode.CompositionResult(map, isNext ? 1 : 0);
            result = JSONUtils.toJSON(map);
            return result;

        } catch (Exception e) {
            //清空map
            map.clear();
            map = OperateCode.CompositionResult(map, -1);
            result = JSONUtils.toJSON(map);
            return result;
        }
    }

    /**
     * 状态改变 ajax异步
     *
     * @param id     记录id
     * @param status 状态
     * @return json
     */
    @RequestMapping(value = "/changeFlag", method = RequestMethod.POST)
    @ResponseBody
    public String openOrStopFlag(Integer id, Integer status) {

        // 结果返回 结果至少包含一个操作状态和一条状态信息
        String result = "";
        Map<String, String> map = new HashMap<String, String>();

        try {
            Boolean isNext = lbsTrackService.changeState(id, status);
            map = OperateCode.CompositionResult(map, isNext ? 1 : 0);
            result = JSONUtils.toJSON(map);
            return result;

        } catch (Exception e) {
            //清空map
            map.clear();
            map = OperateCode.CompositionResult(map, -1);
            result = JSONUtils.toJSON(map);
            return result;
        }
    }
    //endregion

    //region 数据查看

    /**
     * 数据查看
     *
     * @param model     model模型
     * @param id        主键id
     * @param tableName 数据库英文表名
     * @return model
     */
    @RequestMapping(value = "/show", method = RequestMethod.POST)
    public String show(Model model, Integer id, String tableName) {
        id = (id == null ? 0 : id);

        LbsTrackDTO obj = new LbsTrackDTO();
        obj.setId(id);

        if (id > 0) {
            obj = lbsTrackService.getById(id);
        }
        lbsTrackService.assembleObjInfo(model, tableName, obj);

        return "show/show-template";
    }

    //endregion

    //region 轨迹上传

    /**
     * 轨迹上传
     *
     * @param model   model
     * @param trackId 轨迹ID
     * @return Log
     */
    @RequestMapping(value = "/uploadPoint", method = RequestMethod.POST)
    public String uploadPoint(Model model, Integer trackId) {

        model.addAttribute("logName", "上传日志");

        List<String> logList = new ArrayList<>();
        logList.add("轨迹上传开始！");

        if (trackId == null || trackId <= 0) {
            logList.add("缺少参数！");
            return "common/operate-loginfo-modal";
        }

        //[1].获取轨迹
        LbsTrackDTO track = lbsTrackService.getFromCacheId(trackId);
        if (track == null) {
            logList.add("未查询到轨迹信息！");
            return "common/operate-loginfo-modal";
        }

        //[2].获取终端
        LbsTerminalDTO terminal = lbsTerminalService.getFromCacheId(track.getTerminalId());
        if (terminal == null) {
            logList.add("未查询到终端信息！");
            return "common/operate-loginfo-modal";
        }

        //[3].获取服务
        LbsServiceDTO service = lbsServiceService.getFromCacheId(terminal.getServiceId());
        if (service == null) {
            logList.add("未查询到服务信息！");
            return "common/operate-loginfo-modal";
        }

        //[4].获取未上传的坐标点
        int count = 0;
        for (int i = 0; i < 10000; i++) {
            LbsPointDTOQuery pointQuery = new LbsPointDTOQuery();
            pointQuery.setTrackId(trackId);
            pointQuery.setIsUpload(0);
            pointQuery.setPageSize(100);
            Page<LbsPointDTO> pointPage = lbsPointService.getForPage(pointQuery);

            List<Integer> ids = new ArrayList<>();

            List<Map<String, String>> pointList = new ArrayList<>();
            Map<String, String> pointMap;

            for (LbsPointDTO point : pointPage.getItems()) {
                pointMap = new HashMap<>();
                pointMap.put("location", point.getLongitude() + "," + point.getLatitude());
                pointMap.put("locatetime", point.getLocatetime());
                pointMap.put("accuracy", point.getAccuracy() + "");

                BigDecimal speed = point.getSpeed();
                if (speed.compareTo(BigDecimal.ZERO) > 0) {
                    speed = speed.divide(new BigDecimal(1000), 3, BigDecimal.ROUND_HALF_UP);
                }
                pointMap.put("speed", speed.toString());
                pointMap.put("direction", point.getDirection() + "");

                pointList.add(pointMap);
                ids.add(point.getId());
            }

            if (pointList.size() == 0) {
                break;
            }

            //高德坐标上传
            String url = "https://tsapi.amap.com/v1/track/point/upload";
            StringBuilder sb = new StringBuilder();
            sb.append("key=" + KeyConstants.LBS_GAODE_TRACKKEY);
            sb.append("&sid=").append(service.getServiceKey());
            sb.append("&tid=").append(terminal.getTerminalKey());
            sb.append("&trid=").append(track.getTrackKey());
            sb.append("&points=").append(FastJsonUtils.toJSONString(pointList));

            System.out.println("LBS参数：" + sb.toString());
            String res = HttpClient.doPost(url, sb.toString(), 1);

            JSONObject jsonObject = new JSONObject(res);
            if (!jsonObject.get("errcode").equals(10000)) {
                logList.add("高德坐标上传异常：" + jsonObject.get("errdetail"));
                logList.add("错误坐标：" + jsonObject.get("data"));
            }

            //上传成功后，更新is_upload字段
            LbsPointDTOQuery updateQuery = new LbsPointDTOQuery();
            updateQuery.setIds(ids);
            updateQuery.setIsUpload(1);
            lbsPointService.updateInfoByQuery(updateQuery);
        }

        logList.add("轨迹上传结束，本次共上传" + count + "个坐标！");

        model.addAttribute("logList", logList);

        return "common/operate-loginfo-modal";
    }
    //endregion

    //region 轨迹查询

    /**
     * 轨迹查询
     *
     * @param model        model
     * @param trackId      轨迹ID
     * @param daterange    查询时间
     * @param pn           轨迹序号
     * @param btnType      1表示上一条 2表示下一条
     * @param reset        1表示查询重置
     * @param isPartSearch 分轨迹查询，间隔为10分钟
     * @param orderId      工单ID，查询工单的轨迹
     * @return vm
     */
    @RequestMapping(value = "/searchTrack", method = RequestMethod.GET)
    public String searchTrack(Model model, Integer trackId, String daterange, Integer pn, Integer btnType, Integer reset, Integer isPartSearch, Integer orderId) {

        model.addAttribute("amapKey", amapKey);
        model.addAttribute("amapVersion", amapVersion);

        //设定查询的上班时间和下班时间
        String onWorkTime = "08:00:00";
        String offWorkTime = "18:00:00";

        //从缓存中获取配置
        String timeArr = configParamService.getByParamKey("go-work-time", "");
        if (StringUtils.isNotBlank(timeArr) && timeArr.split(",").length == 2) {
            onWorkTime = timeArr.split(",")[0];
            offWorkTime = timeArr.split(",")[1];
        }

        model.addAttribute("onWorkTime", onWorkTime);
        model.addAttribute("offWorkTime", offWorkTime);

        //region [0].初始值

        //轨迹序号
        if (pn == null || pn <= 0) {
            pn = 1;
        }

        //上一条、下一条
        if (btnType == null) {
            btnType = 0;
        }

        //重置查询
        if (reset == null) {
            reset = 0;
        }

        //分轨迹查询
        if (isPartSearch == null) {
            isPartSearch = 0;
        }

        //订单号
        if (orderId == null || orderId < 0) {
            orderId = 0;
        }

        if (trackId == null || trackId <= 0) {
            System.out.println("未获取到轨迹");

            model.addAttribute("startDate", DateConvertUtils.dateToStr(new Date()));
            model.addAttribute("endDate", DateConvertUtils.dateToStr(new Date()));

            model.addAttribute("pn", pn);
            model.addAttribute("isPartSearch", isPartSearch);
            model.addAttribute("orderId", orderId);
            model.addAttribute("returnMessage", "未查询到轨迹！");
            return "lbs/track/search-track";
        }

        model.addAttribute("trackId", trackId);

        //endregion

        //region [1].获取轨迹
        LbsTrackDTO track = lbsTrackService.getFromCacheId(trackId);
        if (track == null) {
            model.addAttribute("returnMessage", "未查询到轨迹信息！");
            return "lbs/track/search-track";
        }
        model.addAttribute("track", track);
        //endregion

        //region [2].日期设置：不能超过上下班时间
        String onWork = track.getCreateTimeSr().split(" ")[0] + " " + onWorkTime;
        String offWork = track.getCreateTimeSr().split(" ")[0] + " " + offWorkTime;

        String startDate = track.getCreateTimeSr();
        if (DateUtils.compareDateTime(startDate, onWork)) { //true表示前面的日期小
            startDate = onWork;
        }

        String endDate = offWork;

        //排序方式
        String orderby = "order by locatetime asc";

        //不管是哪一种查询，都不能超过上下班时间（订单轨迹查询除外）
        if (StringUtils.isNotBlank(daterange) && reset == 0) {

            //直接查询
            String[] date = daterange.split("~");
            if (date.length == 2) {
                startDate = date[0].trim();
                endDate = date[1].trim();
            }

            //点击“上一条轨迹”（倒查）
            if (btnType == 1) {
                orderby = "order by locatetime desc";
                endDate = startDate;
                startDate = onWork; //最早是上班时间
            }

            //点击“下一条轨迹”
            if (btnType == 2) {
                startDate = endDate;
                endDate = offWork; //最晚是下班时间
            }

            //无订单时，按上下班时间
            if (orderId == 0) {
                if (DateUtils.compareDateTime(startDate, onWork)) { //true表示前面的日期小
                    startDate = onWork;
                }
                if (DateUtils.compareDateTime(offWork, endDate)) { //true表示前面的日期小
                    endDate = offWork;
                }
            }
        }
        //endregion

        //region [3].获取坐标点
        LbsPointDTOQuery pointQuery = new LbsPointDTOQuery();
        pointQuery.setTrackId(trackId);
        pointQuery.setStartUnixTime(DateConvertUtils.StrDateToUnix(startDate));
        pointQuery.setEndUnixTime(DateConvertUtils.StrDateToUnix(endDate));
        pointQuery.setPageSize(1000);  //高德地图一次只能显示1000个点，后面有过滤
        pointQuery.setOrderBy(orderby);
        Page<LbsPointDTO> pointPage = lbsPointService.getForPage(pointQuery);

        List<LbsPointDTO> pointList = new ArrayList<>();
        LbsPointDTO temp;
        LbsPointDTO prePoint = new LbsPointDTO();
        LbsPointDTO lastPoint = null;

        for (int i = 0; i < pointPage.getItems().size(); i++) {
            temp = pointPage.getItems().get(i);

            //第一个坐标点
            if (i == 0) {
                temp.setDiffTm(Long.parseLong(temp.getLocatetime()));
                pointList.add(temp);
                prePoint = temp;
                continue;
            }

            //计算两个点的间隔
            long diff = Long.parseLong(temp.getLocatetime()) - Long.parseLong(prePoint.getLocatetime());
            diff = Math.abs(diff / 1000); //取绝对值，因为有倒查。

            //如果时间间隔大于10分钟，说明有多条轨迹，则中断
            if (isPartSearch == 1 && diff > 600L) {
                model.addAttribute("returnMessage", "提示：系统查询到有多条轨迹，已进行分轨迹查询。");
                endDate = temp.getCreateTimeSr();
                break;
            }

            //两个点的轨迹至少要大于3秒
            if (diff > 3L) {
                temp.setDiffTm(diff);
                pointList.add(temp);
                prePoint = temp;

                //最后一个坐标
                lastPoint = temp;

                //最后一个时间
                if (btnType == 1) { //上一条轨迹，是倒查
                    startDate = temp.getCreateTimeSr();
                } else {
                    endDate = temp.getCreateTimeSr();
                }
            }
        }

        model.addAttribute("pointList", pointList);

        //每次最多查询1000个点
        if (pointPage.getTotalItems() > 1000) {
            model.addAttribute("returnMessage", "提示：当前统计坐标点为" + pointPage.getTotalItems() + "个，超过最大值1000，请选择分轨迹查询。");

            if (btnType == 1) { //上一条轨迹，是倒查
                startDate = pointPage.getItems().get(pointPage.getItems().size() - 1).getCreateTimeSr();
            } else {
                endDate = pointPage.getItems().get(pointPage.getItems().size() - 1).getCreateTimeSr();
            }
        }

        // 计算轨迹持续时间
        String continuedtime = "0";
        if (lastPoint != null && pointPage.getItems().size() > 1) {
            long time = lastPoint.getCreateTime().getTime() - pointPage.getItems().get(0).getCreateTime().getTime();
            time = Math.abs(time / 1000 / 60); //取绝对值，因为有倒查
            continuedtime = ConvertUtils.minutesToFormat(Integer.parseInt(time + ""));
        }
        model.addAttribute("continuedtime", continuedtime);

        //endregion

        //region [4].获取两个坐标点间的距离（近似距离）
        //endregion

        //region [4].获取终端（轨迹点太多，免费额度不够，所以不能上传了，所以这里不查询了）
        //LbsTerminalDTO terminal = lbsTerminalService.getFromCacheId(track.getTerminalId());
        //if (terminal == null) {
        //    model.addAttribute("returnMessage", "未查询到终端信息！");
        //    return "lbs/track/search-track";
        //}
        //endregion

        //region [5].获取服务（轨迹点太多，免费额度不够，所以不能上传了，所以这里不查询了）
        //LbsServiceDTO service = lbsServiceService.getFromCacheId(terminal.getServiceId());
        //if (service == null) {
        //    model.addAttribute("returnMessage", "未查询到服务信息！");
        //    return "lbs/track/search-track";
        //}
        //endregion

        //region [6].高德轨迹查询（轨迹点太多，免费额度不够，所以不能上传了，所以这里不查询了）
        //String url = "https://tsapi.amap.com/v1/track/terminal/trsearch";
        //StringBuilder sb = new StringBuilder();
        //sb.append("key=" + KeyConstants.LBS_GAODE_TRACKKEY);
        //sb.append("&sid=").append(service.getServiceKey());
        //sb.append("&tid=").append(terminal.getTerminalKey());
        //sb.append("&trid=").append(track.getTrackKey());
        //sb.append("&starttime=").append(startTime);
        //sb.append("&endtime=").append(endTime);
        //sb.append("&correction=").append("denoise=1,mapmatch=0,attribute=0,threshold=0,mode=driving");
        //sb.append("&page=").append(1);
        //sb.append("&pagesize=").append(998);

        //System.out.println("LBS参数：" + sb.toString());
        //String res = HttpClient.doPost(url, sb.toString(), 1);
        //System.out.println(res);

        //JSONObject jsonObject = new JSONObject(res);
        //if (!jsonObject.get("errcode").equals(10000)) {
        //    model.addAttribute("returnMessage", jsonObject.get("errdetail"));
        //}

        //model.addAttribute("data", jsonObject.getJSONObject("data"));
        //endregion

        model.addAttribute("startDate", startDate);
        model.addAttribute("endDate", endDate);

        model.addAttribute("pn", pn);
        model.addAttribute("btnType", 0);
        model.addAttribute("reset", 0);
        model.addAttribute("isPartSearch", isPartSearch);
        model.addAttribute("orderId", orderId);

        return "lbs/track/search-track";
    }
    //endregion
}